﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using Tao.OpenGl;

namespace RSRenderer.GLSL
{

    #region V1
    /*
    /// <summary>
    /// This encapsulates an GLSL-Shader
    /// </summary>
    public abstract class Shader
    {

        public const int VERTEX_SHADER = 1;
        public const int FRAGMENT_SHADER = 2;

        public Shader(string Source)
        {
            this.Start();
            this.source_ = Source;
        }

        public Shader(FileInfo Source)
        {
            this.Start();

            try
            {
                StreamReader fr = Source.OpenText();
                this.source_ = fr.ReadToEnd();
            }
            catch (System.Exception e)
            {
                throw new Exception("Unable to load Shader from File '" + this.source_.Substring(0, 15) + "'", e);
            }
        }

        private void Start()
        {
            this.source_ = null;
            this.id_ = 0;
            this.inited_ = false;
            this.attached_ = false;
            this.compiled_ = false;
        }

        public virtual void InitShader()
        {
            try
            {
                this.initShader(0);
            }
            catch (System.Exception e)
            {
                throw new Exception("Unable to init Shader from Source '" + this.source_.Substring(0, 15) + "'", e);
            }
        }

        protected virtual void initShader(int ShaderType)
        {

            try
            {
                // This is left in to show how to use the function 
                //returning just a IntPtr, not a string at all. Maybe someone wondered... 
                //At least I did ;-)
                //IntPtr pVersion = Gl.glGetString(Gl.GL_VERSION);
                //string version = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(pVersion);
                string version = Gl.glGetString(Gl.GL_VERSION);

                // First create the Shader-Object (OpenGL-side) to hold the data
                if (ShaderType == VERTEX_SHADER)
                    this.id_ = (uint)Gl.glCreateShaderObjectARB(Gl.GL_VERTEX_SHADER_ARB);
                if (ShaderType == FRAGMENT_SHADER)
                    this.id_ = (uint)Gl.glCreateShaderObjectARB(Gl.GL_FRAGMENT_SHADER_ARB);

#if CHECK_GL_ERRORS
                    Shader.CheckGlError();
#endif

                // Ok, first we clean up a bit as we found that the Windows- \r\n-Sequence
                // could be quite annoying...
                this.source_ = this.source_.Replace("\r", "");

                // Then send the source to the GL-Shader-Object
                Gl.glShaderSourceARB((uint)this.id_, 1, ref this.source_, new int[1] { this.source_.Length });

#if CHECK_GL_ERRORS
                    Shader.CheckGlError();
#endif

                // Now that we've added the source we might want to compile it
                Gl.glCompileShader(this.id_);

#if CHECK_GL_ERRORS
                    Shader.CheckGlError();
#endif

                int value = 0;
                Gl.glGetShaderiv(this.id_, Gl.GL_COMPILE_STATUS, out value);

                this.compiled_ = (value == 1);

#if CHECK_GL_ERRORS
                    Shader.CheckGlError();
#endif

                if (!this.compiled_)
                {
                    Gl.glGetShaderiv((uint)this.id_, Gl.GL_INFO_LOG_LENGTH, out value);

#if CHECK_GL_ERRORS
                        Shader.CheckGlError();
#endif

                    int int_value = 0;
                    string string_value = new string((char)0, value);

                    System.IntPtr pString = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(string_value);

                    Gl.glGetInfoLogARB((int)this.id_, value, out int_value, pString);

                    string result = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(pString);

                    if (result.Length > 0)
                        MessageBox.Show(result, "InfoLog");

#if CHECK_GL_ERRORS
                        Shader.CheckGlError();
#endif

                    this.inited_ = true;
                }
            }
            catch (System.Exception ex)
            {
                throw new System.Exception("Could not init Shader " + this.ToString(), ex);
            }
        }

        // This is only for debugging purposes (I set a breakpoint on the while)
        public static void CheckGlError()
        {

            int errorCode = Gl.GL_NO_ERROR + 1;
            string currentError = "";
            while ((errorCode = Gl.glGetError()) != Gl.GL_NO_ERROR)
                currentError = Glu.gluErrorString(errorCode);

        }

        public bool Inited { get { return this.inited_; } }
        public bool Attached
        {
            get
            {
                return this.attached_;
            }
            set
            {
                this.attached_ = value;
            }
        }

        public uint Id { get { return this.id_; } }
        public bool Compiled { get { return this.compiled_; } }

        protected string source_;
        protected bool inited_;
        protected bool attached_;
        protected uint id_;
        protected bool compiled_;

    }

    public class VertexShader : Shader
    {

        public VertexShader(string Source) : base(Source) { }
        public VertexShader(FileInfo Source) : base(Source) { }

        public override void InitShader()
        {
            try
            {
                this.initShader(Shader.VERTEX_SHADER);
            }
            catch (System.Exception e)
            {
                throw new Exception("Unable to init Vertex-Shader from Source '" + this.source_.Substring(0, 15) + "'", e);
            }
        }

        public override string ToString()
        {
            string result = "VertexShader [";

            if (this.source_ != null)
                if (this.source_.Length > 0)
                    result += "'" + this.source_ + "'";

            result += "]";

            return result;

        }

    }

    public class FragmentShader : Shader
    {
        public FragmentShader(string Source) : base(Source) { }
        public FragmentShader(FileInfo Source) : base(Source) { }

        public override void InitShader()
        {
            try
            {
                this.initShader(Shader.FRAGMENT_SHADER);
            }
            catch (System.Exception e)
            {
                throw new Exception("Unable to init Fragment-Shader from Source '" + this.source_.Substring(0, 15) + "'", e);
            }
        }

        public override string ToString()
        {
            string result = "FragmentShader [";

            if (this.source_ != null)
                if (this.source_.Length > 0)
                    result += "'" + this.source_ + "'";

            result += "]";

            return result;
        }

    }

    public class GLSLShader
    {

        public GLSLShader(VertexShader vertexShader, FragmentShader fragmentShader)
        {

            this.id_ = 0;
            this.linked_ = false;

            this.vertexShader_ = vertexShader;
            this.fragmentShader_ = fragmentShader;

            if (this.vertexShader_ != null)
                this.vertexShader_.InitShader();
            if (this.fragmentShader_ != null)
                this.fragmentShader_.InitShader();

            this.init();
        }

        protected virtual void init()
        {
            string vertexLog = "";
            string fragmentLog = "";
            string linkLog = "";

            try
            {
                this.id_ = (uint)Gl.glCreateProgram();

                if (this.vertexShader_ != null)
                {
                    if (this.vertexShader_.Compiled)
                    {
                        Gl.glAttachObjectARB(this.id_, this.vertexShader_.Id);
                    }
                }

                if (this.fragmentShader_ != null)
                {
                    if (this.fragmentShader_.Compiled)
                    {
                        Gl.glAttachObjectARB(this.id_, this.fragmentShader_.Id);
                    }
                }

                int[] attachedShaders = new int[2];
                int count = 0;
                Gl.glGetAttachedShaders((int)this.id_, 2, out count, attachedShaders);

                for (int i = 0; i < count; i++)
                {
                    if (this.vertexShader_ != null)
                        if (this.vertexShader_.Id == attachedShaders[i])
                            this.vertexShader_.Attached = true;
                    if (this.fragmentShader_ != null)
                        if (this.fragmentShader_.Id == attachedShaders[i])
                            this.fragmentShader_.Attached = true;
                }

                if (this.vertexShader_.Attached || this.fragmentShader_.Attached)
                {
                    Gl.glLinkProgramARB(this.id_);

#if CHECK_GL_ERRORS
                        Shader.CheckGlError();
#endif

                    int value = 0;
                    Gl.glGetObjectParameterivARB(this.id_, Gl.GL_OBJECT_LINK_STATUS_ARB, out value);

                    this.linked_ = (value == 1);

#if CHECK_GL_ERRORS
                        Shader.CheckGlError();
#endif

                    if (!this.linked_)
                    {
                        Gl.glGetObjectParameterivARB(this.id_, Gl.GL_OBJECT_INFO_LOG_LENGTH_ARB, out value);

#if CHECK_GL_ERRORS
                            Shader.CheckGlError();
#endif

                        int int_value = 0;
                        string string_value = new string((char)0,
value);
                        System.IntPtr pString = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(string_value);

                        Gl.glGetInfoLogARB((int)this.id_, value, out int_value, pString);

                        string result = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(pString);

                        if (result.Length > 0)
                            MessageBox.Show(result, "InfoLog");

                        throw new System.Exception("Could not link Shader-Program!");
                    }

                }
                else
                    throw new System.Exception("No attached Shaders");

            }
            catch (System.Exception ex)
            {
                string message = "Could not create Shader-Program from Shaders";
                if (this.vertexShader_ != null)
                    message += "\n\n" + this.vertexShader_.ToString();
                if (this.fragmentShader_ != null)
                    message += "\n\n" + this.fragmentShader_.ToString();
                if (vertexLog.Length > 0)
                    message += "\n\n" + vertexLog;
                if (fragmentLog.Length > 0)
                    message += "\n\n" + fragmentLog;
                if (linkLog.Length > 0)
                    message += "\n\n" + linkLog;
                throw new Exception(message, ex);
            }
        }

        public bool Use()
        {
            if (this.linked_)
                Gl.glUseProgramObjectARB(this.id_);

            return this.linked_;
        }

        public static void FixedFunction()
        {
            Gl.glUseProgramObjectARB(0);
        }

        private VertexShader vertexShader_;
        private FragmentShader fragmentShader_;

        private uint id_;
        private bool linked_;

    }
    */
    #endregion

    /*
public class Shader
>	{
>		int mHandleVert;
>		int mHandleFrag;
>		int mHandleProg;
>
>		public Shader()
>		{
>			mHandleVert = -1;
>			mHandleFrag = -1;
>			mHandleProg = -1;
>		}
>
>		public int LoadShader(string fragFile, string vertFile)
>		{
>			VirtualFileSystem vfs = VirtualFileSystem.GetInstance();
>			
>			StreamReader vs = vfs.OpenTextFile(vertFile);
>			string vertexsource = vs.ReadToEnd();
>			vs.Close();
>
>			vs = vfs.OpenTextFile(fragFile);
>			string fragsource = vs.ReadToEnd();
>			vs.Close();
>
>			mHandleVert = Gl.glCreateShaderObjectARB(Gl.GL_VERTEX_SHADER_ARB);
>			mHandleFrag = Gl.glCreateShaderObjectARB(Gl.GL_FRAGMENT_SHADER_ARB);
>
>			Gl.glShaderSourceARB(mHandleVert, 1, ref vertexsource, null);
>			Gl.glShaderSourceARB(mHandleFrag, 1, ref fragsource, null);
>
>			Gl.glCompileShaderARB(mHandleVert);
>			Gl.glCompileShaderARB(mHandleFrag);
>
>			mHandleProg = Gl.glCreateProgramObjectARB();
>			Gl.glAttachObjectARB(mHandleProg, mHandleVert);
>			Gl.glAttachObjectARB(mHandleProg, mHandleFrag);
>
>			Gl.glLinkProgramARB(mHandleProg);
>
>			return mHandleProg;
>		}
>	}


     * >Gl.glUseProgramObjectARB(shaderProgHandle); // the shader prog you want to use ( as returned by Shader.LoadShader(...) )
>Gl.glUniform4fARB(Gl.glGetUniformLocationARB(shaderProgHandle, "BaseColor"), r , g , b, 1.0f); // pass the information to the shader you might want to use
>
>// draw your stuff  using the shader here
>
>Gl.glUseProgramObjectARB(0); // disable the shader usage

     */

}
